<?php
// +--------------------------------------------------------------------------------------------
// | Author: Admin <543423@qq.com>
// +--------------------------------------------------------------------------------------------
// | Copyright ThinkAdmin http://www.thinkadmin.cn All rights reserved.
// +--------------------------------------------------------------------------------------------

/**
 * +--------------------------------------------------------------------------------------------
 * | 项目公共函数库
 * +--------------------------------------------------------------------------------------------
 * | 项目公共函数库对整个项目都可调用
 * +--------------------------------------------------------------------------------------------
 */

/**
 * 获取系统信息
 * return array
 */
function get_sysinfo() {
	$sys_info['zlib']           = function_exists('gzclose');//zlib
	$sys_info['safe_mode']      = (boolean) ini_get('safe_mode');//safe_mode = Off
	$sys_info['safe_mode_gid']  = (boolean) ini_get('safe_mode_gid');//safe_mode_gid = Off
	$sys_info['timezone']       = function_exists("date_default_timezone_get") ? date_default_timezone_get() : L('no_setting');
	$sys_info['socket']         = function_exists('fsockopen') ;
	$sys_info['web_server']     = $_SERVER['SERVER_SOFTWARE'];
	$sys_info['phpv']           = phpversion();
	$sys_info['fileupload']     = @ini_get('file_uploads') ? ini_get('upload_max_filesize') :'unknown';
	return $sys_info;
}

/**
 *
 * 改造U函数，使其完美支持自定义的路由
 * @param string $url URL表达式，格式：'[模块/控制器/操作#锚点@域名]?参数1=值1&参数2=值2...'
 * @param string|array $vars 传入的参数，支持数组和字符串
 * @param string $suffix 伪静态后缀，默认为true表示获取配置值
 * @param boolean $domain 是否显示域名
 * @return string
 */
function URL($url	=	'',	$vars	=	'',	$suffix	=	true,	$domain	=	false){
	return U($url,	$vars,	$suffix,	$domain);
}

/**
 *
 * 钩子调用函数
 * @param $type	钩子类型， 如插件 Plugin 工具Tool 等
 * @param $name 调用的钩子标识名，目录名
 * @param $action 调用的控制器操作方法名
 * @param $controller 调用的控制器类文件名 默认为Run 即 Run.class.php
 * @param $hookaction 钩子方法 默认call 
 * @param $params 要传递的参数
 * @param $status	是否限制调用状态为使用  设置为true表示当前调用需要调用的程序状态在正常使用情况下才调用，设置为false表示不限制状态都可调用
 * @param $access true 权限访问调用 false 不使用外部调用限制  true开启外部调用限制 
 */
function HOOK($type='', $name='', $action='', $controller='Run', $hookaction='call', $params=null, $status = true, $access = false){
	//if(empty($type) || empty($name) || empty($action)) return null;
	$name = ucfirst(strtolower($name));
	$controller = empty($controller) ? 'Run' : ucfirst(strtolower($controller));
	$hookaction = empty($hookaction) ? 'call' : $hookaction;
	$hook = new \Common\Lib\Hook($type,$name); //实例钩子类
	return $hook->$hookaction($type, $name, $action, $controller='Run', $params, $status, $access);
}

/**
 *
 * 密码格式检测
 * @param $password  检测的密码
 */
function is_format_password($password = ''){
	if(get_strlens($password) < (int)C('PASSWORD_MIN_LENGTH') || get_strlens($password) > (int)C('PASSWORD_MAX_LENGTH')){  //长度不正确
		return array(0,L('PASSWORD_LEN_ERROR_TIP_A').C('PASSWORD_MIN_LENGTH').'-'.C('PASSWORD_MAX_LENGTH').L('LEN_ERROR_TIP_B'));
	}
	return array(1,L('PASSWORT_FORMA_SUCCESS'));
}

/**
 *
 * 获取字符长度
 * @param $str 传入字符串
 * @param $charset 编码
 * @return 返回该字符串的长度
 */
function get_strlens($str='', $charset='utf-8')
{
	$re['utf-8']   = "/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xff][\x80-\xbf]{3}/";
	$re['gb2312'] = "/[\x01-\x7f]|[\xb0-\xf7][\xa0-\xfe]/";
	$re['gbk']    = "/[\x01-\x7f]|[\x81-\xfe][\x40-\xfe]/";
	$re['big5']   = "/[\x01-\x7f]|[\x81-\xfe]([\x40-\x7e]|\xa1-\xfe])/";
	empty($charset) && $charset = C('DEFAULT_CHARSET');
	preg_match_all($re[$charset], $str, $match);
	return count($match[0]);
}

/**
 * 全局加密规则
 * @$value string  要加密的变量
 * @return string 加密后的字符串
 */
function get_md5_rules($value=''){
	return md5(md5($value).md5(C('ENCRYPTION_FACTOR').md5($value.C('ENCRYPTION_FACTOR'))));
}

/**
 *
 * 权限检查方法
 * @param $rules 规则，数组，支持多个规则
 * @param $type 验证类型 支持or 与and
 * @param $roleid 验证的角色id
 */
function RULES_AUTH($rules = array(),$type='and',$roleId=''){
	empty($roleId) && $roleId = session(C('USER_INFO').'.role_id');
	//超级管理不受任何限制
	if($roleId == C('ADMIN_ROLE_ID')) return true;
	if(empty($rules)) return false; //没有指定规则
	$role = new \Common\Model\SystemRoleModel;
	$verify = $role->verifyAuth($rules,$roleId);
	if(!$verify) return false;
	if(strtolower($type) =='and'){ //如果是and条件所有条件必须通过
		return count($rules) == count($verify) ? true : false;
	}else{//只要有满足的条件即可，只要$verify不是空数组即可通过
		return !empty($verify) ? true : false;

	}
}

/**
 *
 * 字符串截取
 * @param $str  截取的字符串
 * @param $start  截取起始位置
 * @param $length  截取长度
 * @param $charset  字符编码
 * @param $suffix   是否添加省略号
 */
function msubstr($str='', $start=0, $length, $charset="utf-8", $suffix=false) {
	empty($charset) && $charset = C('DEFAULT_CHARSET');
	if(get_strlens($str)<=$length){
		return $str;
	}
	if(function_exists("mb_substr"))
	$slice = mb_substr($str, $start, $length, $charset);
	elseif(function_exists('iconv_substr')) {
		$slice = iconv_substr($str,$start,$length,$charset);
		if(false === $slice) {
			$slice = '';
		}
	}else{
		$re['utf-8']   = "/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xff][\x80-\xbf]{3}/";
		$re['gb2312'] = "/[\x01-\x7f]|[\xb0-\xf7][\xa0-\xfe]/";
		$re['gbk']    = "/[\x01-\x7f]|[\x81-\xfe][\x40-\xfe]/";
		$re['big5']   = "/[\x01-\x7f]|[\x81-\xfe]([\x40-\x7e]|\xa1-\xfe])/";
		preg_match_all($re[$charset], $str, $match);
		$slice = join("",array_slice($match[0], $start, $length));
	}
	return $suffix ? $slice.$suffix : $slice;
}

/**
 *
 * 获取远程内容
 * @param $url 地址
 * @param $cache 是否缓存
 */
function get_contents($url = '',$cache = false){
	if($cache){
		$con = S(get_md5_rules($url).md5($url));
		if(!$con){
			if(!link_url($url)) { //不可链接
				$con = '';
			}else{
				$con = file_get_contents($url);
				S(get_md5_rules($url).md5($url),$con);
			}
		}
	}else{
		if(!link_url($url)) { //不可链接
			$con = '';
		}else{
			$con = file_get_contents($url);
			S(get_md5_rules($url).md5($url),$con);
		}
	}
	return $con;
}

/**
 *
 * 检测网络链接地址是否可链接
 * @param $url
 */
function link_url($url){
	return @fopen($url,"r") ? true : false;
}

/**
 *
 * 把菜单数组处理成树
 * @param $arr  数组
 * @param $pid  父级
 * @param $new_arr  新数组
 */
function menutree($arr=array(),$pid=0,$new_arr=array()){
	if(is_array($arr)){
		foreach($arr as $k=>$v){
			if($v['menu_pid']==$pid){
				(($arr[$k]['menu_fun'] == 1) && !empty($arr[$k]['menu_url']) ) &&  $arr[$k]['menu_url'] = MENUURL(str_replace('&amp;', '&', $arr[$k]['menu_url']));
				$arr[$k]['menu_id'] = $v['menu_id'].'_menu';
				$arr[$k]['mid'] = $v['menu_id'];
				$new_arr[$v['menu_id'].'_menu']=$arr[$k];
				$new_arr[$v['menu_id'].'_menu']['menu_items']=menutree($arr,$v['menu_id']);
			}
		}
	}
	return $new_arr ? $new_arr : array();
}

/**
 * 菜单规则支持路由函数
 * @$url 菜单URL
 */
function MENUURL($url = ''){
	return URL($url);
}

/**
 *
 * 文件大小单位转换
 * @size 大小，字节
 */
function formatBytes($size) {
	$units = array('B', 'KB', 'MB', 'GB', 'TB');
	for ($i = 0; $size >= 1024 && $i < 4; $i++) $size /= 1024;
	return round($size, 2).$units[$i];
}

/**
 *
 * 获取文件类型
 * @param $filename //文件路径 只支持当前现有的文件后缀，其他不准确
 */
function get_file_type($filename)
{
	$file = fopen($filename, "rb");
	$bin = fread($file, 2); //只读2字节
	fclose($file);
	$strInfo = @unpack("C2chars", $bin);
	$typeCode = intval($strInfo['chars1'].$strInfo['chars2']);
	$fileType = '';
	switch ($typeCode)
	{
		case 7790:
			$fileType = 'exe';
			break;
		case 7784:
			$fileType = 'midi';
			break;
		case 8297:
			$fileType = 'rar';
			break;
		case 8075:
			$fileType = 'zip';
			break;
		case 255216:
			$fileType = 'jpg';
			break;
		case 7173:
			$fileType = 'gif';
			break;
		case 6677:
			$fileType = 'bmp';
			break;
		case 13780:
			$fileType = 'png';
			break;
		default:
			$fileType = 'unknown: '.$typeCode;
	}

	//Fix
	if ($strInfo['chars1']=='-1' AND $strInfo['chars2']=='-40' ) return 'jpg';
	if ($strInfo['chars1']=='-119' AND $strInfo['chars2']=='80' ) return 'png';

	return $fileType;
}

/**
 *
 * 名称格式检测，2～20位的中文字母数字不能输入特殊符号
 * @param $name  检测的字符串
 */
function is_format_name($name=""){
	return preg_match('/^[\x{4e00}-\x{9fa5}A-Za-z0-9]{1,16}+$/u', $name) ? true : false;
}

/**
 * 对查询结果集进行排序
 * @access public
 * @param array $list 查询结果
 * @param string $field 排序的字段名
 * @param array $sortby 排序类型
 * asc正向排序 desc逆向排序 nat自然排序
 * @return array
 */
function list_sort_by($list,$field, $sortby='asc') {
	if(is_array($list)){
		$refer = $resultSet = array();
		foreach ($list as $i => $data)
			$refer[$i] = &$data[$field];
		switch ($sortby) {
			case 'asc': // 正向排序
				asort($refer);
				break;
			case 'desc':// 逆向排序
				arsort($refer);
				break;
			case 'nat': // 自然排序
				natcasesort($refer);
				break;
		}
		foreach ( $refer as $key=> $val)
			$resultSet[] = &$list[$key];
		return $resultSet;
	}
	return false;
}

/**
 * 获取数组键值
 */
function getArraykey($arr=array(),$key){
	$newarr=array_keys($arr);
	return $newarr[$key];
}

/**
 * 字符串转换为数组，主要用于把分隔符调整到第二个参数
 * @param  string $str  要分割的字符串
 * @param  string $glue 分割符
 * @return array
 * @author PHP@妖孽  <dino_ma@163.com>
 */
function str2arr($str, $glue = ','){
    return explode($glue, $str);
}

/**
 * 数组转换为字符串，主要用于把分隔符调整到第二个参数
 * @param  array  $arr  要连接的数组
 * @param  string $glue 分割符
 * @return string
 * @author PHP@妖孽  <dino_ma@163.com>
 */
function arr2str($arr, $glue = ','){
    return implode($glue, $arr);
}

/**
 * 时间戳格式化
 * @param int $time
 * @return string 完整的时间显示
 * @author PHP@妖孽 <dino_ma@163.com>
 */
function time_format($time = NULL,$format='Y-m-d H:i:s'){
	$time = $time === NULL ? NOW_TIME : intval($time);
	return date($format, $time);
}

/**
 * @todo 二维数组去重
 * @param array $array
 * @return array
 * @author Msj
 */
function arrayUnique($array){
	foreach ($array as $k => $v) {
		foreach ($array as $k2 => $v2) {
			if (($v2 == $v) && ($k != $k2)) {
				unset($array[$k]);
			}
		}
	}
	return array_merge($array);
}

/**
 * @todo 二维数组处理成字符串
 * @param array $array
 * @return array
 * @author PHP@妖孽 <dino_ma@163.com>
 */
function array_implode($arrays, &$target = array()) {
	foreach ($arrays as $item) {
		if (is_array($item)) {
			array_implode($item, $target);
		} else {
			$target[] = $item;
		}
	}
	return $target;
}

/**
 * 检查字符串是否是UTF8编码
 * @param string $string 字符串
 * @return Boolean
 */
function is_utf8($string) {
	return preg_match('%^(?:
         [\x09\x0A\x0D\x20-\x7E]            # ASCII
       | [\xC2-\xDF][\x80-\xBF]             # non-overlong 2-byte
       |  \xE0[\xA0-\xBF][\x80-\xBF]        # excluding overlongs
       | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}  # straight 3-byte
       |  \xED[\x80-\x9F][\x80-\xBF]        # excluding surrogates
       |  \xF0[\x90-\xBF][\x80-\xBF]{2}     # planes 1-3
       | [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
       |  \xF4[\x80-\x8F][\x80-\xBF]{2}     # plane 16
    )*$%xs', $string);
}

/**
 * 代码加亮
 * @param String  $str 要高亮显示的字符串 或者 文件名
 * @param Boolean $show 是否输出
 * @return String
 */
function highlight_code($str,$show=false) {
	if(file_exists($str)) {
		$str    =   file_get_contents($str);
	}
	$str  =  stripslashes(trim($str));
	// The highlight string function encodes and highlights
	// brackets so we need them to start raw
	$str = str_replace(array('&lt;', '&gt;'), array('<', '>'), $str);

	// Replace any existing PHP tags to temporary markers so they don't accidentally
	// break the string out of PHP, and thus, thwart the highlighting.

	$str = str_replace(array('&lt;?php', '?&gt;',  '\\'), array('phptagopen', 'phptagclose', 'backslashtmp'), $str);

	// The highlight_string function requires that the text be surrounded
	// by PHP tags.  Since we don't know if A) the submitted text has PHP tags,
	// or B) whether the PHP tags enclose the entire string, we will add our
	// own PHP tags around the string along with some markers to make replacement easier later

	$str = '<?php //tempstart'."\n".$str.'//tempend ?>'; // <?

	// All the magic happens here, baby!
	$str = highlight_string($str, TRUE);

	// Prior to PHP 5, the highlight function used icky font tags
	// so we'll replace them with span tags.
	if (abs(phpversion()) < 5) {
		$str = str_replace(array('<font ', '</font>'), array('<span ', '</span>'), $str);
		$str = preg_replace('#color="(.*?)"#', 'style="color: \\1"', $str);
	}

	// Remove our artificially added PHP
	$str = preg_replace("#\<code\>.+?//tempstart\<br />\</span\>#is", "<code>\n", $str);
	$str = preg_replace("#\<code\>.+?//tempstart\<br />#is", "<code>\n", $str);
	$str = preg_replace("#//tempend.+#is", "</span>\n</code>", $str);

	// Replace our markers back to PHP tags.
	$str = str_replace(array('phptagopen', 'phptagclose', 'backslashtmp'), array('&lt;?php', '?&gt;', '\\'), $str); //<?
	$line   =   explode("<br />", rtrim(ltrim($str,'<code>'),'</code>'));
	$result =   '<div class="code"><ol>';
	foreach($line as $key=>$val) {
		$result .=  '<li>'.$val.'</li>';
	}
	$result .=  '</ol></div>';
	$result = str_replace("\n", "", $result);
	if( $show!== false) {
		echo($result);
	}else {
		return $result;
	}
}


/**
 * 在数据列表中搜索
 * @access public
 * @param array $list 数据列表
 * @param mixed $condition 查询条件
 * 支持 array('name'=>$value) 或者 name=$value
 * @return array
 */
function list_search($list,$condition) {
	if(is_string($condition))
		parse_str($condition,$condition);
	// 返回的结果集合
	$resultSet = array();
	foreach ($list as $key=>$data){
		$find   =   false;
		foreach ($condition as $field=>$value){
			if(isset($data[$field])) {
				if(0 === strpos($value,'/')) {
					$find   =   preg_match($value,$data[$field]);
				}elseif($data[$field]==$value){
					$find = true;
				}
			}
		}
		if($find)
			$resultSet[]     =   &$list[$key];
	}
	return $resultSet;
}

// 自动转换字符集 支持数组转换
function auto_charset($fContents, $from='gbk', $to='utf-8') {
	$from = strtoupper($from) == 'UTF8' ? 'utf-8' : $from;
	$to = strtoupper($to) == 'UTF8' ? 'utf-8' : $to;
	if (strtoupper($from) === strtoupper($to) || empty($fContents) || (is_scalar($fContents) && !is_string($fContents))) {
		//如果编码相同或者非字符串标量则不转换
		return $fContexnts;
	}
	if (is_string($fContents)) {
		if (function_exists('mb_convert_encoding')) {
			return mb_convert_encoding($fContents, $to, $from);
		} elseif (function_exists('iconv')) {
			return iconv($from, $to, $fContents);
		} else {
			return $fContents;
		}
	} elseif (is_array($fContents)) {
		foreach ($fContents as $key => $val) {
			$_key = auto_charset($key, $from, $to);
			$fContents[$_key] = auto_charset($val, $from, $to);
			if ($key != $_key)
				unset($fContents[$key]);
		}
		return $fContents;
	}
	else {
		return $fContents;
	}
}